/* eslint-disable no-restricted-globals */
/* eslint-disable no-param-reassign */
import { createUUID } from 'qx-util';
import {
  ValueOP,
  ControlVO,
  CodeListItem,
  GridController,
  ISearchCondField,
  MDCtrlLoadParams,
} from '@ibiz-template/runtime';
import { clone, plus } from '@ibiz-template/core';
import {
  IGroupData,
  IOdooGridState,
  OdooLoadParams,
  IApiMDGroupParams,
} from './interface';
import { formatDate, getDateRange } from './util';

/**
 * @description Odoo分组表格控制器
 * @export
 * @class OdooGroupGridController
 * @extends {GridController}
 */
export class OdooGroupGridController extends GridController {
  /**
   * @description Odoo表格状态
   * @type {IOdooGridState}
   * @memberof OdooGridController
   */
  declare state: IOdooGridState;

  /**
   * @description 分组参数
   * @private
   * @type {IApiMDGroupParams[]}
   * @memberof OdooGridController
   */
  private groupParams: IApiMDGroupParams[] = [];

  /**
   * @description 数据缓存map
   * @private
   * @type {Map<string, ControlVO[]>} key为搜索条件，value为对应的数据集合
   * @memberof OdooGroupGridController
   */
  private dataMap: Map<string, ControlVO[]> = new Map();

  /**
   * @description 代码表缓存
   * @private
   * @type {Map<string, readonly CodeListItem[]>}
   * @memberof OdooGridController
   */
  private codelistMap: Map<string, readonly CodeListItem[]> = new Map();

  protected initState(): void {
    super.initState();
    this.initStateData();
  }

  protected initStateData() {
    this.state.groups = [];
    this.state.items = [];
    this.state.selectedData = [];
    this.state.expandItems = [];
    this.state.checkboxSelected = false;
    this.state.selectAll = false;
    this.state.enableGroup = this.groupParams?.length > 0;
    this.state.size = this.model.pagingSize || 20;
  }

  /**
   * @description 执行分组
   * @param {IApiMDGroupParams[]} arg
   * @param {IParams} [_params]
   * @returns {*}  {Promise<void>}
   * @memberof OdooGridController
   */
  async execGroup(arg: IApiMDGroupParams[], _params?: IParams): Promise<void> {
    this.groupParams = arg;
    this.initStateData();
    await this.load({ isInitialLoad: true });
  }

  /**
   * 加载数据
   * @param args
   * @returns
   */
  async load(args: OdooLoadParams = {}): Promise<IData[]> {
    const { isInitialLoad } = args;
    if (isInitialLoad) this.dataMap.clear();
    if (this.state.enableGroup) {
      this.state.groups = await this.loadGroup();
    } else {
      await this.loadData(args);
      this.state.expandItems = this.state.items;
    }
    return this.state.items;
  }

  /**
   * @description 加载树数据
   * @param group 分组
   * @returns
   */
  async loadTreeData(group: IGroupData): Promise<IData[]> {
    const { searchconds = [], level = 0, children } = group;
    if (children.length) return children;
    let result: IData[] = [];
    const nextLevel = level + 1;
    if (nextLevel < this.groupParams.length) {
      result = await this.loadGroup(nextLevel, searchconds);
    } else {
      result = await this.loadData({ searchconds, isLoadTree: true });
    }
    return result;
  }

  /**
   * 计算指标参数
   */
  calcBimeasures(): { name: string; aggmode?: string }[] {
    const bimeasures: { name: string; aggmode?: string }[] = [
      { name: 'srfcount' },
    ];
    Object.values(this.fieldColumns).forEach(column => {
      const { aggField, aggMode } = column.model;
      if (aggField)
        bimeasures.push({
          name: aggField,
          aggmode: aggMode,
        });
    });
    return bimeasures;
  }

  /**
   * @description 加载分组
   * @param level 层级
   * @param searchCond 查询条件
   * @returns
   */
  async loadGroup(
    level: number = 0,
    searchconds: ISearchCondField[] = [],
  ): Promise<IGroupData[]> {
    let result: IGroupData[] = [];
    const { groupFieldId, dateFormat } = this.groupParams[level];
    const { id, codeName } = this.dataEntity;
    // *查询参数处理
    const { context } = this.handlerAbilityParams();
    const params = await this.getFetchParams();
    const bimeasures = this.calcBimeasures();
    Object.assign(params, {
      size: 1000,
      bimeasures,
      bicubemode: 1,
      bicubetag: codeName?.toLowerCase(),
      bidimensions: [{ name: groupFieldId }],
    });
    if (searchconds.length) {
      if (params.searchconds && params.searchconds[0]) {
        params.searchconds[0].searchconds.push(...searchconds);
      } else {
        Object.assign(params, {
          searchconds: [
            {
              condtype: 'GROUP',
              condop: 'AND',
              searchconds,
            },
          ],
        });
      }
    }
    const app = ibiz.hub.getApp(this.context.srfappid);
    // 查询分组数据
    const res = await app.deService.exec(id!, 'fetchdefault', context, params);
    if (res.ok && Array.isArray(res.data)) {
      if (dateFormat?.length) {
        result = this.handleGroupByDate(res.data, level, searchconds);
      } else {
        result = await this.handleGroupByField(res.data, level, searchconds);
      }
      // 查询顶层数据时设置total
      if (level === 0) {
        const total = res.data.reduce(
          (sum, item) => sum + (Number(item.srfcount) || 0),
          0,
        );
        this.state.total = total;
        this.state.size = total;
        this.state.totalPages = 1;
      }
    }
    return result;
  }

  /**
   * 计算聚合值
   * @param items
   * @returns
   */
  private calcAggValue(items: IData[]): IData {
    const aggResult: IData = {};
    Object.values(this.fieldColumns).forEach(column => {
      const { aggField, aggValueFormat, unitName, aggMode } = column.model;
      if (aggField) {
        let aggValue: number | undefined;
        switch (aggMode) {
          case 'SUM':
            aggValue = items
              .map(item => item[aggField] as number)
              .reduce((a, b) => {
                const aValue = Number(a) || 0;
                const bValue = Number(b) || 0;
                return plus(aValue, bValue);
              }, 0);
            break;
          case 'AVG':
            aggValue =
              items
                .map(item => item[aggField] as number)
                .reduce((a, b) => {
                  const aValue = Number(a) || 0;
                  const bValue = Number(b) || 0;
                  return plus(aValue, bValue);
                }, 0) / items.length;
            break;
          case 'MAX':
            aggValue = Math.max(...items.map(item => item[aggField] as number));
            break;
          case 'MIN':
            aggValue = Math.min(...items.map(item => item[aggField] as number));
            break;
          default:
            break;
        }
        let value = `${aggValue}`;
        if (aggValueFormat) {
          try {
            value = ibiz.util.text.format(`${aggValue}`, aggValueFormat);
          } catch (error) {
            ibiz.log.error(
              ibiz.i18n.t('runtime.controller.control.grid.formattingError', {
                aggValue,
              }),
            );
          }
        }
        if (aggValue && unitName) value += unitName;
        aggResult[aggField] = value;
      }
    });
    return aggResult;
  }

  /**
   * 处理分组（根据时间类型）
   * @param items 分组数据
   * @param level 层级
   * @param searchconds 父级条件
   * @returns
   */
  private handleGroupByDate(
    items: IData[],
    level: number = 0,
    searchconds: ISearchCondField[] = [],
  ): IGroupData[] {
    const rootNodes: IGroupData[] = [];
    const { groupFieldId, dateFormat = [] } = this.groupParams[level];
    const valueFieldId = groupFieldId.toLowerCase();
    // 构建树结构
    const tree = new Map<
      string,
      {
        value: string;
        date: string;
        count: number;
        parentPath: string;
        format: 'year' | 'quarter' | 'month' | 'week' | 'day';
        children: IData[];
      }
    >();
    items.forEach(item => {
      const date = item[valueFieldId];
      if (!date || isNaN(new Date(date).getTime())) return;
      const count = Number(item.srfcount) || 0;
      let parentPath = '';
      dateFormat.forEach(format => {
        const value = formatDate(date, format);
        const path = parentPath ? `${parentPath}|${value}` : value;
        if (!tree.has(path)) {
          tree.set(path, {
            date,
            value,
            format,
            count: 0,
            parentPath,
            children: [],
          });
        }
        tree.get(path)!.count += count;
        tree.get(path)!.children.push(item);
        parentPath = path;
      });
    });
    const nodeMap = new Map<string, IGroupData>();
    // 第一遍：创建所有分组
    tree.forEach((node, path) => {
      const key = createUUID();
      const dataRange = getDateRange(node.date, node.format);
      const aggResult = this.calcAggValue(node.children);
      const groupNode: IGroupData = {
        key,
        level,
        value: node.value,
        caption: node.value,
        children: [],
        tempsrfkey: key,
        hasChildren: true,
        count: node.count,
        searchconds: [
          ...clone(searchconds),
          {
            condtype: 'DEFIELD',
            condop: ValueOP.GT_AND_EQ,
            fieldname: valueFieldId,
            value: dataRange!.start,
          },
          {
            condtype: 'DEFIELD',
            condop: ValueOP.LT_AND_EQ,
            fieldname: valueFieldId,
            value: dataRange!.end,
          },
        ],
        ...aggResult,
      };
      nodeMap.set(path, groupNode);
    });

    // 第二遍：建立父子关系
    tree.forEach((node, path) => {
      if (!node.parentPath) {
        rootNodes.push(nodeMap.get(path)!);
      } else {
        const parentNode = nodeMap.get(node.parentPath);
        if (parentNode) {
          parentNode.children.push(nodeMap.get(path)!);
        }
      }
    });
    // 对每个层级的子节点进行排序
    const sortChildren = (nodes: IGroupData[]): void => {
      nodes.sort((a, b) => a.value!.localeCompare(b.value!));
      nodes.forEach(node => sortChildren(node.children as IGroupData[]));
    };
    sortChildren(rootNodes);
    return rootNodes;
  }

  /**
   * 处理分组（根据属性值）
   * @param items 分组数据
   * @param level 层级
   * @param searchconds 父级条件
   * @returns
   */
  private async handleGroupByField(
    items: IData[],
    level: number,
    searchconds: ISearchCondField[],
  ): Promise<IGroupData[]> {
    const result: IGroupData[] = [];
    const { groupFieldId, groupCodeListId } = this.groupParams[level];
    const app = ibiz.hub.getApp(this.context.srfappid);
    // 预加载代码表
    if (groupCodeListId && !this.codelistMap.has(groupCodeListId)) {
      const codeListItems = await app.codeList.get(
        groupCodeListId,
        this.context,
      );
      this.codelistMap.set(groupCodeListId, codeListItems);
    }
    const codeList = groupCodeListId
      ? this.codelistMap.get(groupCodeListId) || []
      : [];
    const valueFieldId = groupFieldId.toLowerCase();
    const textFieldId = `${valueFieldId}_text`;
    items.forEach(item => {
      const key = createUUID();
      const value = item[valueFieldId];
      const codeListItem = codeList.find(x => x.value === value);
      const caption =
        codeListItem?.text || item[textFieldId] || value || '未分类';
      const aggResult = this.calcAggValue([item]);
      result.push({
        key,
        level,
        value,
        caption,
        children: [],
        tempsrfkey: key,
        hasChildren: true,
        count: Number(item.srfcount),
        searchconds: [
          ...clone(searchconds),
          {
            value,
            condtype: 'DEFIELD',
            condop: value ? ValueOP.EQ : ValueOP.IS_NULL,
            fieldname: valueFieldId,
          },
        ],
        ...aggResult,
      });
    });
    // 将未分类移动到最后
    const index = result.findIndex(item => !item.value);
    if (index !== -1) result.push(result.splice(index, 1)[0]);
    return result;
  }

  /**
   * 加载数据
   * @param searchCond
   * @returns
   */
  async loadData(args: OdooLoadParams): Promise<ControlVO[]> {
    await this.startLoading();
    let data: ControlVO[] = [];
    try {
      const { size, isLoadTree, searchconds, isSearch } = args;
      const isInitialLoad = args.isInitialLoad === true;
      const isLoadMore = args.isLoadMore === true;
      if (isInitialLoad || isLoadTree || isSearch) {
        this.state.curPage = 1;
      } else if (isLoadMore) {
        this.state.curPage += 1;
      }
      const { context } = this.handlerAbilityParams();
      const params = await this.getFetchParams();
      if (searchconds?.length) {
        if (!params.searchconds)
          Object.assign(params, {
            searchconds: [
              {
                condtype: 'GROUP',
                condop: 'AND',
                searchconds: [],
              },
            ],
          });
        // 附加分组条件
        params.searchconds[0].searchconds.push(...searchconds);
        Object.assign(params, { size: 1000 });
      }
      if (size) Object.assign(params, { size });
      const res = await this.service.fetch(context, params);
      data = res.data;
      if (!isLoadTree && !isSearch) {
        // 初始化查询更新分页数据总条数
        if (typeof res.total === 'number') {
          this.state.total = res.total;
        }
        if (typeof res.totalx === 'number') {
          this.state.totalx = res.totalx;
        }
        if (typeof res.totalPages === 'number') {
          this.state.totalPages = res.totalPages;
        }
      }
      if (!isSearch) {
        if (isLoadMore || isLoadTree) {
          this.state.items.push(...data);
        } else {
          this.state.items = data;
        }
      }
      await this.afterLoad(
        Object.assign(args, { isLoadMore: isLoadMore || isLoadTree }),
        data,
      );
      await this._evt.emit('onLoadSuccess', {
        isInitialLoad,
      });
    } catch (error) {
      await this._evt.emit('onLoadError', undefined);
      this.actionNotification('FETCHERROR', {
        error: error as Error,
      });
      throw error;
    } finally {
      this.state.isLoaded = true;
      await this.endLoading();
    }

    this.state.items.forEach((item, index) => {
      item.srfserialnum = index + 1;
    });

    this.actionNotification('FETCHSUCCESS');
    return data;
  }

  /**
   * @description 计算分组数据（重写置空）
   * @protected
   * @param {IData[]} _items
   * @memberof OdooGridController
   */
  protected calcGroupData(_items: IData[]): void {}

  /**
   * 本地排序items（重写置空，后续版本没有该方法）
   * @param items
   */
  sortItems(items: IData[]): void {}

  handleRefreshMode(args: MDCtrlLoadParams): void {}

  /**
   * 处理全选改变
   * @param state 
   */
  async handleSelectAllChange(state: boolean): Promise<void> {
    this.state.selectAll = state;
    if (state) {
      // 如果是数据全选状态，则需请求后台获取所有数据设置为选中数据，并设置缓存
      const key = JSON.stringify(this.state.searchParams);
      let data: ControlVO[] = [];
      if (this.dataMap.has(key)) {
        data = this.dataMap.get(key) || [];
      } else {
        data = await this.loadData({ isSearch: true, size: 1000 });
        this.dataMap.set(key, data);
      }
      this.setSelection(data);
    }
  }
}
